//=============================================================================
// filename: player.cpp
// description: source file for our player engine
// autor: Jay Tomten (jaytomten@gmail.com)
//=============================================================================

#include <GL/glu.h>
#include <iostream>
#include "object.h"
#include "input.h"
#include "level.h"
#include "world.h"
#include "character.h"
#include "particle.h"
#include "player.h"
#include "game.h"

//=============================================================================
// constructor
Player::Player()
    {
    // nothing to do...
    }

//=============================================================================
// destructor
Player::~Player()
    {
    delete pList; // this will not delete!?
    pEvents.clear();
    delete &pUI;
    delete &pStats;
    delete &pCamera;

    }

//=============================================================================
// Init() - set up our player class
bool Player::Init(Game *engine, int playerType, Player *opponent)
    {
    bool alive = true;
    //
    // save references to parameters
    //
    rEngine = engine;
    pStats.type = playerType;
    rOpponent = opponent;
    //
    // initialize player vars and list
    //
    pList = new List();
    pStats.units = 0;
    pStats.fobs = 0;
    if  (rEngine->state == GAME_MULTI_PLAYER_LOCAL || rEngine->state == GAME_MAIN_MENU)
        {
        pStats.points = 0;
        pStats.kills = 0;
        }
    pStats.influence = 100.0;
    checkQuit = false;
    //
    // do player type dependant loading
    //
    if  (pStats.type == PLAYER_0)
        {
        pUI.state = MENU_STATE_OFF;
        cout << "Loading player 0...  success. \n";
        }
    else if (pStats.type == PLAYER_1 && rEngine->state == GAME_STORY)
        {
        alive = pCamera.Init();
        alive = pUI.Init(rEngine, PLAYER_1, &pEvents, &pStats);                 // build our user interfaces
        pUI.state = MENU_STATE_OFF;
        cout << "Loading player 1 story...  success. \n";
        }
    else if (pStats.type == PLAYER_1)
        {
        alive = pCamera.Init();
        alive = pUI.Init(rEngine, PLAYER_1, &pEvents, &pStats);                 // build our user interfaces
        if(rEngine->state == GAME_MAIN_MENU) pUI.state = MENU_STATE_MM;
        //else if(rEngine->state == GAME_STORY) pUI.state = MENU_STATE_OFF;
        else if(rEngine->state == GAME_SINGLE_PLAYER) pUI.state = MENU_STATE_GM;
        else if(rEngine->state == GAME_MULTI_PLAYER_LOCAL) pUI.state = MENU_STATE_GM;
        cout << "Loading player 1...  success. \n";
        Color *yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f);
        Vertex *v = new Vertex(-100.0f, 3.0f, 0.0f);
        pUI.Bubble("Controls are W S A D E Q", v, yellow);
        }
    else if (pStats.type == PLAYER_2)
        {
        alive = pCamera.Init();
        alive = pUI.Init(rEngine, PLAYER_2, &pEvents, &pStats); // build our user interfaces
        if  (rEngine->state == GAME_MAIN_MENU) pUI.state = MENU_STATE_OFF;
        else if(rEngine->state == GAME_SINGLE_PLAYER) pUI.state = MENU_STATE_OFF;
        else if(rEngine->state == GAME_MULTI_PLAYER_LOCAL) pUI.state = MENU_STATE_GM;
        cout << "Loading player 2...  success. \n";
        Color *yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f);
        Vertex *v = new Vertex(100.0f, 3.0f, 0.0f);
        pUI.Bubble("Controls are I K J L O U", v, yellow);
        }

    // game state dependant loading
    if  (rEngine->state == GAME_MULTI_PLAYER_LOCAL)
        {
        pUI.width /= 2; // crappy fix... should be done in interface class
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0f, ((float)rEngine->gScreen.width / 2.0f) / (float)rEngine->gScreen.height, 0.01f, 500.0f);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        cout << "Split screen OpenGL setup...  success. \n";
        }
    else if (pStats.type != PLAYER_0)     // if not split screen multiplayer
        {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0f, (float)rEngine->gScreen.width / (float)rEngine->gScreen.height, 0.01f, 500.0f);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        cout << "Standard OpenGL screen setup...  success. \n";
        }

    return alive;

    }

//=============================================================================
// Update() - update all of this players stuff and draw to screen
bool Player::Update(float d)
    {

    bool alive = true;

    // update the view ports and the UI // calculate influence
    pStats.influence += pStats.fobs * 1.6 * d;
    pStats.influence -= pStats.units * 0.3 * d;
    checkUI = false; // if  (pStats.type == PLAYER_0) checkUI = false;
    if  (pStats.type == PLAYER_1
    &&   (rEngine->state == GAME_MULTI_PLAYER_LOCAL || rEngine->state == GAME_MULTI_LOCAL_P2_WIN || rEngine->state == GAME_MULTI_LOCAL_P1_WIN))
        {
        glViewport(0, 0, rEngine->gScreen.width / 2, rEngine->gScreen.height);
        if(alive) alive = pUI.Update(d);
        checkUI = true;
        }
    else if (pStats.type == PLAYER_2)
        {
        glLoadIdentity();                                         // lets do it again this loop to clear the camera stuff
        glViewport(rEngine->gScreen.width / 2, 0, rEngine->gScreen.width / 2, rEngine->gScreen.height);
        if(alive) alive = pUI.Update(d);
        checkUI = true;
        }
    else if (pStats.type == PLAYER_1)     // if we just have 1 player and its not multiplayer mode update the UI
        {
        if(alive) alive = pUI.Update(d);
        checkUI = true;
        }

    // update camera
    if  (pStats.type != PLAYER_0 && alive) alive = pCamera.Update(d);

    // collide this player and opponet objects, only take action on this players objects
    if  (alive) Collide(d, rOpponent);

    // check to see if there were any events selected in the UI and process them
    if  (!pEvents.empty() && alive) alive = Process();

    // run through the entire list for this player
    Node *curr = NULL;
    Node *dead = NULL;
    curr = pList->front;
    while (curr != NULL)
        {
        // if Draw returns false (for a dead object) set current and remove dead node
        if(!curr->o->Update(d))
            {
            dead = curr;
            if(dead->o->t >= 3000 && dead->o->t < 4000 && (dead->o->pos.x < 98.0f && dead->o->pos.x > -98.0f))
                {
                World *w = new World(WORLD_GRASS_1, dead->o->pos.x - 0.5f, 0.0f, 0.0f);
                rEngine->gList->InsertAfter(w, rEngine->gList->front->next);
                Particle *p = new Particle(PARTICLE_BLOOD, dead->o->pos.x, dead->o->pos.y + 0.3, 0.0);
                pList->InsertFront(p);
                rEngine->gSound.PlaySFX(SFX_DIE2);
                rOpponent->pStats.points += 5;
                rOpponent->pStats.kills++;
                }
            pList->Remove(dead); // finally remove the dead object
            }
        curr = curr->next;
        }

    return alive;

    }

//=============================================================================
// Draw() - draw all of this players stuff
void Player::Draw()
    {
    Node *curr = NULL;                                          // the nodes used to traverse our list of objects & remove dead objects
    curr = pList->front;                                        // run through the entire list for this player
    while (curr != NULL)
        {
        curr->o->Draw();
        // if the screen settings are good enough (smooth = true != 0) draw a bunch of strings to the screen
        if  (rEngine->gScreen.smooth > 0)
            {
            static char c[50];

            // draw character life
            if  (curr->o->t >= 3000 && curr->o->t < 4000)
                {
                sprintf(c, "%.0f", curr->o->life);
                if  (pStats.type == PLAYER_1 || pStats.type == PLAYER_2)
                    {
                    pUI.DrawString3D(curr->o->pos.x + 0.9f, curr->o->pos.y - 0.5f, 0.0f, c, FONT_MAIN16);
                    }
                else if (pStats.type == PLAYER_0)
                    {
                    rOpponent->pUI.DrawString3D(curr->o->pos.x + 0.9f, curr->o->pos.y - 0.5f, 0.0f, c, FONT_MAIN16);
                    }
                }

            // draw building life
            if  (curr->o->t > 2000 && curr->o->t < 2900)
                {
                if(pStats.type == PLAYER_1) sprintf(c, "%.0f", -curr->o->life);
                else sprintf(c, "%.0f", curr->o->life);
                if(pStats.type == PLAYER_1)                 pUI.DrawString3D(curr->o->pos.x - 1.0f, curr->o->pos.y + 0.1f, 0.0f, c, FONT_MAIN16);
                else if(pStats.type == PLAYER_2)            pUI.DrawString3D(curr->o->pos.x - 1.0f, curr->o->pos.y + 0.1f, 0.0f, c, FONT_MAIN16);
                else if(pStats.type == PLAYER_0) rOpponent->pUI.DrawString3D(curr->o->pos.x - 1.0f, curr->o->pos.y + 0.1f, 0.0f, c, FONT_MAIN16);
                }
            }

        curr = curr->next;

        } // end while

    // clean up
    delete curr;

    if  (checkUI
    &&   rEngine->state != GAME_STORY)
        {
        pUI.Draw();
        checkUI = false;
        }

    }

//=============================================================================
// Process() - process the UI and other events captured
bool Player::Process()
    {

    bool alive = true;
    float startPoint = 0.0f;
    Color *yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f);
    Color *red = new Color(1.0f, 0.0f, 0.0f, 1.0f);
    bool checkQuitStart = checkQuit;

    // as long as it is not the computer player, process the events
    if  (pStats.type != PLAYER_0)
        {
        for (list<int>::iterator i = pEvents.begin(); i != pEvents.end(); i++)
            {
            if(*i == SELECT_NEW_SOLO_GAME)
                {
                rEngine->nextLevel = LEVEL_STORY_1;
                rEngine->reload = true;
                rEngine->state = GAME_STORY;
                }
            else if(*i == SELECT_NEW_MULTI_GAME)
                {
                rEngine->nextLevel = LEVEL_3;
                rEngine->reload = true;
                rEngine->state = GAME_MULTI_PLAYER_LOCAL;
                pEvents.erase(i);
                break;
                }
            else if(*i == SELECT_END_MATCH)
                {
                if(checkQuitStart == true) pStats.fobs = 0;
                else
                    {
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    pUI.Message("Select Again to END", red);
                    checkQuit = true;
                    }
                }
            else if(*i == SELECT_EXIT_GAME)
                {
                rEngine->state = GAME_CLOSING;
                }
            else if(*i == SELECT_BUILD_INF)
                {
                if(pStats.influence > 0.0 && pStats.building <= pStats.fobs)
                    {
                    pUI.Message("Building Infantryman", yellow);
                    rEngine->gSound.PlaySFX(SFX_BLIP);
                    // set a character start point depending on player type
                    if(pStats.type == PLAYER_2) startPoint = 99.0f;
                    if(pStats.type == PLAYER_1) startPoint = -99.0f;
                    Character *a = new Character(CHAR_INFANTRY, startPoint, 0.0f, 0.0f);
                    pStats.influence -= a->cost;
                    pList->InsertBack(a);
                    pStats.points++;
                    }
                else if(pStats.influence < 0.0)
                    {
                    pUI.Message("Insufficient Influence", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                else if(pStats.building >= pStats.fobs)
                    {
                    pUI.Message("Max Units Building", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                }
            else if(*i == SELECT_BUILD_ENG)
                {
                if(pStats.influence > 0.0 && pStats.building <= pStats.fobs)
                    {
                    pUI.Message("Building Engineer", yellow);
                    rEngine->gSound.PlaySFX(SFX_BLIP);
                    // set a character start point depending on player type
                    if(pStats.type == PLAYER_2) startPoint = 99.0f;
                    if(pStats.type == PLAYER_1) startPoint = -99.0f;
                    Character *a = new Character(CHAR_ENGINEER, startPoint, 0.0f, 0.0f);
                    pStats.influence -= a->cost;
                    pList->InsertBack(a);
                    pStats.points++;
                    }
                else if(pStats.influence < 0.0)
                    {
                    pUI.Message("Insufficient Influence", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                else if(pStats.building >= pStats.fobs)
                    {
                    pUI.Message("Max Units Building", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                }
            else if(*i == SELECT_BUILD_MED)
                {
                if(pStats.influence > 0.0 && pStats.building <= pStats.fobs)
                    {
                    pUI.Message("Building Medic", yellow);
                    rEngine->gSound.PlaySFX(SFX_BLIP);
                    // set a character start point depending on player type
                    if(pStats.type == PLAYER_2) startPoint = 99.0f;
                    if(pStats.type == PLAYER_1) startPoint = -99.0f;
                    Character *a = new Character(CHAR_MEDIC, startPoint, 0.0f, 0.0f);
                    pStats.influence -= a->cost;
                    pList->InsertBack(a);
                    pStats.points++;
                    }
                else if(pStats.influence < 0.0)
                    {
                    pUI.Message("Insufficient Influence", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                else if(pStats.building >= pStats.fobs)
                    {
                    pUI.Message("Too Many Units Building", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                }
            else if(*i == SELECT_BUILD_EOD)
                {
                if(pStats.influence > 0.0 && pStats.building <= pStats.fobs)
                    {
                    pUI.Message("Building EOD Tech", yellow);
                    rEngine->gSound.PlaySFX(SFX_BLIP);
                    // set a character start point depending on player type
                    if(pStats.type == PLAYER_2) startPoint = 99.0f;
                    if(pStats.type == PLAYER_1) startPoint = -99.0f;
                    Character *a = new Character(CHAR_EOD, startPoint, 0.0f, 0.0f);
                    pStats.influence -= a->cost;
                    pList->InsertBack(a);
                    pStats.points++;
                    }
                else if(pStats.influence < 0.0)
                    {
                    pUI.Message("Insufficient Influence", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                else if(pStats.building >= pStats.fobs)
                    {
                    pUI.Message("Max Units Building", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                }
            else if(*i == SELECT_BUILD_SF)
                {
                if(pStats.influence > 0.0 && pStats.building <= pStats.fobs)
                    {
                    pUI.Message("Building SF Soldier", yellow);
                    rEngine->gSound.PlaySFX(SFX_BLIP);
                    // set a character start point depending on player type
                    if(pStats.type == PLAYER_2) startPoint = 99.0f;
                    if(pStats.type == PLAYER_1) startPoint = -99.0f;
                    Character *a = new Character(CHAR_SF, startPoint, 0.0f, 0.0f);
                    pStats.influence -= a->cost;
                    pList->InsertBack(a);
                    pStats.points++;
                    }
                else if(pStats.influence < 0.0)
                    {
                    pUI.Message("Insufficient Influence", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                else if(pStats.building >= pStats.fobs)
                    {
                    pUI.Message("Max Units Building", red);
                    rEngine->gSound.PlaySFX(SFX_BLEEP);
                    }
                }
            else if(*i == SELECT_CAMERA_IN)  pCamera.In();
            else if(*i == SELECT_CAMERA_OUT)   pCamera.Out();
            else if(*i == SELECT_CAMERA_LEFT)  pCamera.Left();
            else if(*i == SELECT_CAMERA_RIGHT) pCamera.Right();
            }

        pEvents.clear(); // once we are done clear the events

        }

    if  (checkQuitStart == true) checkQuit = false;

    return alive;

    }

//=============================================================================
// Collide() - perform collision dectection - the big ugly, non sexy version
//             only perform actions initiated by this players objects
void Player::Collide(float d, Player *enemy)
    {

    Node *curr; // this player
    Node *good; // iterator for teammates
    Node *bad;  // the aggressor
    Node *ugly; // level/neutral objects

    int previousState = 0; // save previous state

    if(d > 0.5f)
        {
        cerr << "Delta is very high: d = " << d << endl;
        d = 0.0f;
        }

    // run collision, code "duplication" among different types more efficient in this case
    // look into a way to make player class more generic
    if(pStats.type == PLAYER_1)
        {

        // reset this players stats
        pStats.units = 0;
        pStats.fobs = 0;
        pStats.building = 0;

        curr = pList->front;
        while(curr)
            {

            // only run collision detection with characters as main actors
            // get some stats!
            if(curr->o->t >  2000 && curr->o->t < 2900) pStats.fobs++;
            if(curr->o->t >= 3000 && curr->o->t < 4000)
                {
                pStats.units++;
                if(curr->o->state == STATE_BUILDING)        pStats.building++;

                // save previous state, and reset state for collision detection
                previousState = curr->o->state;
                if(curr->o->state != STATE_BUILDING) curr->o->state = STATE_NORM;

                if(curr->o->power > 36.0f || curr->o->range > 8.0f)
                    {
                    cerr << "Mutant alert! \n    Power = " << (float)curr->o->power << "\n    Range = " << (float)curr->o->range << "\n    Type = " << (int)curr->o->t << endl;
                    curr->o->life = 0.0f;
                    }

                // run though teammembers list
                good = pList->front;
                while(good)
                    {

                    // medics heal team members
                    if((good->o->t >= 3000 && good->o->t <= 3004) || (good->o->t >= 2001 && good->o->t <= 2005))
                        {
                        if(curr->o->t == CHAR_MEDIC && curr->o->state != STATE_BUILDING)
                            {
                            if(good->o->t >= 3000 && good->o->t <= 3999)   // is it a character?
                                {
                                if(curr->o->pos.x + curr->o->range > good->o->pos.x && curr->o->pos.x < good->o->pos.x && good->o->life < 120.0 && good->o != curr->o)
                                    {
                                    curr->o->state = STATE_HEALING;
                                    if(previousState != STATE_HEALING && rand()%4 == 1)
                                        {
                                        Color *c = new Color(0.0f, 1.0f, 0.0f, 1.0f);
                                        Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                        if(rand()%2 == 1) pUI.Bubble("Be careful out there!", v, c);
                                        else pUI.Bubble("I am treating your wounds...", v, c);
                                        }
                                    good->o->life += d * (curr->o->power / 2);
                                    }
                                }

                            // engineers fix team buildings
                            }
                        else if(curr->o->t == CHAR_ENGINEER && curr->o->state != STATE_BUILDING)
                            {
                            if(good->o->t > 2000 && good->o->t < 2900)   // is it a building?
                                {
                                if(curr->o->pos.x + curr->o->range / 4 > good->o->pos.x && good->o->life > -100.0 && curr->o->pos.x < good->o->pos.x)
                                    {
                                    curr->o->state = STATE_FIXING;
                                    good->o->life -= d * curr->o->power;
                                    }
                                }
                            }
                        }
                    good = good->next;

                    } // end while(good)

                // run through opponent list
                bad = enemy->pList->front;
                while(bad)
                    {

                    // shoot at anything on the opponents team (including buildings)
                    if((bad->o->t >= 3000 && bad->o->t <= 3004) || (bad->o->t >= 2001 && bad->o->t <= 2005))
                        {
                        if(curr->o->pos.x + curr->o->range > bad->o->pos.x && curr->o->pos.x < bad->o->pos.x)
                            {
                            if(curr->o->state == STATE_NORM)   // make sure they havent already fired
                                {

                                // eod guys damage buildings
                                if(curr->o->t == CHAR_EOD && bad->o->t > 2000 && bad->o->t < 2900 && bad->o->team == TEAM_BAD)
                                    {
                                    bad->o->life -= d * (curr->o->power / 2);
                                    if(bad->o->life < 10.0f) bad->o->team = TEAM_NEUTRAL;
                                    curr->o->state = STATE_ENGAGING;
                                    if(previousState != STATE_ENGAGING && rand()%6 == 1)
                                        {
                                        Color *c = new Color(0.0f, 1.0f, 0.0f, 1.0f);
                                        Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                        if(rand()%2 == 1) pUI.Bubble("Setting charges...", v, c);
                                        else pUI.Bubble("Demo this enemy FOB!", v, c);
                                        }

                                    // standard char vs. char engagement
                                    }
                                else if(bad->o->t >= 3000 && bad->o->t < 4000)
                                    {
                                    bad->o->life -= d * curr->o->power;
                                    curr->o->state = STATE_ENGAGING;
                                    // if this character just started enagaing draw some bullets!
                                    if(previousState != STATE_ENGAGING)
                                        {
                                        Particle *p = new Particle(PARTICLE_BULLETS_F, curr->o->pos.x, curr->o->pos.y + 0.0f, 0.0);
                                        pList->InsertBack(p);
                                        rEngine->gSound.PlaySFX(SFX_GUN1);
                                        if(rand()%6 == 1)
                                            {
                                            Color *c = new Color(0.0f, 1.0f, 0.0f, 1.0f);
                                            Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                            if(rand()%2 == 1) pUI.Bubble("You DIE!", v, c);
                                            else pUI.Bubble("Take this!", v, c);
                                            }
                                        }
                                    }

                                // if team has switched to neutral (building damaged) then do some effects
                                if(bad->o->team == TEAM_NEUTRAL && bad->o->t > 2000 && bad->o->t < 2900)
                                    {
                                    rEngine->gList->InsertBack(bad->o);
                                    enemy->pList->Remove(bad);
                                    Particle *p = new Particle(PARTICLE_SMOKE, bad->o->pos.x, bad->o->pos.y + 1.0, 0.0);
                                    rEngine->gList->InsertBack(p);
                                    p = new Particle(PARTICLE_EXPLOSION, bad->o->pos.x, bad->o->pos.y + 1.0, 0.0);
                                    rEngine->gList->InsertBack(p);
                                    rEngine->gSound.PlaySFX(SFX_EXPLODE);
                                    Color *yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f);
                                    pUI.Message("Enemy FOB Damaged", yellow);
                                    pStats.points += 20;
                                    }
                                }
                            }
                        }

                    bad = bad->next;

                    } // end while(bad)

                // run through neutral list
                ugly = rEngine->gList->front;
                while(ugly)
                    {

                    // capture neutral buildings
                    if((ugly->o->t >= 3000 && ugly->o->t <= 3004) || (ugly->o->t >= 2001 && ugly->o->t <= 2005))
                        {
                        if(curr->o->t == CHAR_ENGINEER && curr->o->state != STATE_BUILDING)
                            {
                            if(ugly->o->t > 2000 && ugly->o->t < 2900)   // is it a building?
                                {
                                if(curr->o->pos.x + curr->o->range / 4 > ugly->o->pos.x && curr->o->pos.x < ugly->o->pos.x)
                                    {
                                    curr->o->state = STATE_FIXING;
                                    ugly->o->life -= d * curr->o->power;
                                    if(previousState != STATE_FIXING && rand()%4 == 1)
                                        {
                                        Color *c = new Color(0.0f, 1.0f, 0.0f, 1.0f);
                                        Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                        if(rand()%2 == 1) pUI.Bubble("This FOB is ours now!", v, c);
                                        else pUI.Bubble("Capturing FOB now...", v, c);
                                        }
                                    // add some effects once neutral buildings captured
                                    if(ugly->o->team == TEAM_GOOD)
                                        {
                                        pList->InsertBack(ugly->o);
                                        rEngine->gList->Remove(ugly);
                                        Color *yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f);
                                        pUI.Message("FOB Captured", yellow);
                                        pStats.points += 10;
                                        }
                                    }
                                }
                            }
                        }

                    ugly = ugly->next;

                    } // end while(ugly)

                } // end character if block

            curr = curr->next;

            } // while(curr) for PLAYER_1

        // now run through the computer or player 2 list and check against good/bad/ugly
        }
    else if(pStats.type != PLAYER_1)
        {

        // reset this players stats
        pStats.units = 0;
        pStats.fobs = 0;
        pStats.building = 0;

        curr = pList->front;
        while(curr)
            {

            // only do collision detection with characters (on everything else)
            // count up stats
            if(curr->o->t >  2000 && curr->o->t < 2900) pStats.fobs++;
            if(curr->o->t >= 3000 && curr->o->t < 4000)
                {
                pStats.units++;
                if(curr->o->state == STATE_BUILDING) pStats.building++;

                // save previous state, and reset state for collision detection
                previousState = curr->o->state;
                if(curr->o->state != STATE_BUILDING) curr->o->state = STATE_NORM;

                // run through this player's good list
                good = pList->front;
                while(good)
                    {

                    // heal good members
                    if((good->o->t >= 3000 && good->o->t <= 3004) || (good->o->t >= 2001 && good->o->t <= 2005))
                        {
                        if(curr->o->t == CHAR_MEDIC && curr->o->state != STATE_BUILDING)
                            {
                            if(good->o->t >= 3000 && good->o->t <= 3999)   // is it a character?
                                {
                                if(curr->o->pos.x - curr->o->range < good->o->pos.x && curr->o->pos.x > good->o->pos.x && good->o->life < 120.0 && good->o != curr->o)
                                    {
                                    curr->o->state = STATE_HEALING;
                                    good->o->life += d * (curr->o->power / 2);
                                    if(previousState != STATE_HEALING && rand()%4 == 1)
                                        {
                                        Color *c = new Color(1.0f, 0.0f, 0.0f, 1.0f);
                                        Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                        if(rEngine->state == GAME_MULTI_PLAYER_LOCAL)
                                            {
                                            if(rand()%2 == 1) pUI.Bubble("Be careful out there!", v, c);
                                            else pUI.Bubble("I am treating your wounds...", v, c);
                                            }
                                        else
                                            {
                                            if(rand()%2 == 1) enemy->pUI.Bubble("Be careful out there!", v, c);
                                            else enemy->pUI.Bubble("I am treating your wounds...", v, c);
                                            }
                                        }
                                    }
                                }

                            // fix our buildings
                            }
                        else if(curr->o->t == CHAR_ENGINEER && curr->o->state != STATE_BUILDING)
                            {
                            if(good->o->t > 2000 && good->o->t < 2900)   // is it a building?
                                {
                                if((curr->o->pos.x - curr->o->range / 4) < (good->o->pos.x) && good->o->life < 100.0 && curr->o->pos.x > good->o->pos.x)
                                    {
                                    curr->o->state = STATE_FIXING;
                                    good->o->life += d * curr->o->power;
                                    }
                                }
                            }
                        }

                    good = good->next;

                    } // end while(good)

                // run through the opposing player's list
                bad = enemy->pList->front;
                while(bad)
                    {

                    // shoot at anything on the opponents team in range if we haven't already shot at something
                    if((bad->o->t >= 3000 && bad->o->t <= 3004) || (bad->o->t >= 2001 && bad->o->t <= 2005))
                        {
                        if(curr->o->pos.x - curr->o->range < bad->o->pos.x && curr->o->pos.x > bad->o->pos.x)
                            {
                            if(curr->o->state == STATE_NORM)   // make sure they havent already fired
                                {

                                // eod get to blow up buildings here
                                if(curr->o->t == CHAR_EOD && bad->o->t > 2000 && bad->o->t < 2900 && bad->o->team == TEAM_GOOD)
                                    {
                                    bad->o->life += d * (curr->o->power / 2);
                                    if(bad->o->life > -10.0f) bad->o->team = TEAM_NEUTRAL;
                                    curr->o->state = STATE_ENGAGING;
                                    if(previousState != STATE_ENGAGING)
                                        {
                                        if(rand()%6 == 1)
                                            {
                                            Color *c = new Color(1.0f, 0.0f, 0.0f, 1.0f);
                                            Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                            if(rEngine->state == GAME_MULTI_PLAYER_LOCAL)
                                                {
                                                if(rand()%2 == 1) pUI.Bubble("Setting charges...", v, c);
                                                else pUI.Bubble("Demo this enemy FOB!", v, c);
                                                }
                                            else
                                                {
                                                if(rand()%2 == 1) enemy->pUI.Bubble("Setting charges...", v, c);
                                                else enemy->pUI.Bubble("Demo this enemy FOB!", v, c);
                                                }
                                            }
                                        }

                                    // standard char vs. char engagements
                                    }
                                else if(bad->o->t >= 3000 && bad->o->t < 4000)
                                    {
                                    bad->o->life -= d * curr->o->power;
                                    curr->o->state = STATE_ENGAGING;
                                    // if this character just started enagaing draw some bullets!
                                    if(previousState != STATE_ENGAGING)
                                        {
                                        Particle *p = new Particle(PARTICLE_BULLETS_E, curr->o->pos.x, curr->o->pos.y + 0.0f, 0.0);
                                        pList->InsertBack(p);
                                        rEngine->gSound.PlaySFX(SFX_GUN3);
                                        if(previousState != STATE_ENGAGING && rand()%6 == 1)
                                            {
                                            Color *c = new Color(1.0f, 0.0f, 0.0f, 1.0f);
                                            Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                            if(rEngine->state == GAME_MULTI_PLAYER_LOCAL)
                                                {
                                                if(rand()%2 == 1) pUI.Bubble("Take this!", v, c);
                                                else pUI.Bubble("You DIE!", v, c);
                                                }
                                            else
                                                {
                                                if(rand()%2 == 1) enemy->pUI.Bubble("Take this!", v, c);
                                                else enemy->pUI.Bubble("You DIE!", v, c);
                                                }
                                            }
                                        }
                                    }

                                // add some sweet effects for items that have switched to neutral (buildings blown by eod)
                                if(bad->o->team == TEAM_NEUTRAL && bad->o->t > 2000 && bad->o->t < 2900)
                                    {
                                    rEngine->gList->InsertBack(bad->o);
                                    enemy->pList->Remove(bad);
                                    Particle *p = new Particle(PARTICLE_SMOKE, bad->o->pos.x, bad->o->pos.y + 1.0, 0.0);
                                    rEngine->gList->InsertBack(p);
                                    p = new Particle(PARTICLE_EXPLOSION, bad->o->pos.x, bad->o->pos.y + 1.0, 0.0);
                                    rEngine->gList->InsertBack(p);
                                    rEngine->gSound.PlaySFX(SFX_EXPLODE);
                                    Color *yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f);
                                    Color *red = new Color(1.0f, 0.0f, 0.0f, 1.0f);
                                    if(rEngine->state == GAME_MULTI_PLAYER_LOCAL)
                                        {
                                        pUI.Message("Enemy Building Damaged", yellow);
                                        pStats.points += 20;
                                        }
                                    else enemy->pUI.Message("FOB Lost", red);
                                    }
                                }
                            }
                        }

                    bad = bad->next;

                    } // end while(bad)

                // move through all the neutral objects
                ugly = rEngine->gList->front;
                while(ugly)
                    {

                    // capture neutral buildings
                    if((ugly->o->t >= 3000 && ugly->o->t <= 3004) || (ugly->o->t >= 2001 && ugly->o->t <= 2005))
                        {
                        if(curr->o->t == CHAR_ENGINEER && curr->o->state != STATE_BUILDING)
                            {
                            if(ugly->o->t > 2000 && ugly->o->t < 2900)   // is it a building?
                                {
                                if((curr->o->pos.x - (curr->o->range / 4)) < (ugly->o->pos.x) && curr->o->pos.x > ugly->o->pos.x)
                                    {
                                    curr->o->state = STATE_FIXING;
                                    ugly->o->life += d * curr->o->power;
                                    if(previousState != STATE_FIXING && rand()%4 == 1)
                                        {
                                        Color *c = new Color(1.0f, 0.0f, 0.0f, 1.0f);
                                        Vertex *v = new Vertex(curr->o->pos.x, curr->o->pos.y + 2.0f, curr->o->pos.z);
                                        if(rEngine->state == GAME_MULTI_PLAYER_LOCAL)
                                            {
                                            if(rand()%2 == 1) pUI.Bubble("This FOB is ours now!", v, c);
                                            else pUI.Bubble("Capturing FOB now...", v, c);
                                            }
                                        else
                                            {
                                            if(rand()%2 == 1) enemy->pUI.Bubble("This FOB is ours now!", v, c);
                                            else enemy->pUI.Bubble("Capturing FOB now...", v, c);
                                            }
                                        }
                                    // add some sweet effects once captured
                                    if(ugly->o->team == TEAM_BAD)
                                        {
                                        pList->InsertBack(ugly->o);
                                        rEngine->gList->Remove(ugly);
                                        Color *yellow = new Color(1.0f, 1.0f, 0.0f, 1.0f);
                                        Color *red = new Color(1.0f, 0.0f, 0.0f, 1.0f);
                                        if(rEngine->state == GAME_MULTI_PLAYER_LOCAL)
                                            {
                                            pUI.Message("Building Captured", yellow);
                                            pStats.points += 10;
                                            }
                                        else enemy->pUI.Message("FOB Captured by Enemy", red);
                                        }
                                    }
                                }
                            }
                        }

                    ugly = ugly->next;

                    } // end while(ugly)

                } // end character if block

            curr = curr->next;

            } // end while(curr) for PLAYER_0/2

        } // end if/else

    // delete curr;
    // delete good;
    // delete bad;
    // delete ugly;

    }


